/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <unistd.h>
#include "iservice_registry.h"
#include "i_sle_host.h"
#include "sle_host_impl.h"
#include "sle_common_config.h"
#include "sle_hilog.h"
#include "sle_hisysevent.h"

namespace OHOS {
namespace NearLink {
namespace SleStandard {
SleHost::SleHostImpl::SleHostImpl()
{
    SLE_LOGI("SleHostImpl In");
    callbackImpl_ = new SleHostCallbackStubImpl(*this);
    SLE_ASSERT_AND_RETURN_LOG(callbackImpl_, "callbackImpl null error!");

    auto resetCallback = [this](const wptr<IRemoteObject> &remote) { this->ResetService(remote); };
    deathRecipient_ = new SleDeathRecipient(resetCallback);
    SLE_ASSERT_AND_RETURN_LOG(deathRecipient_, "deathRecipient null error!");

    std::lock_guard<std::mutex> lock(mutexProxy_);
    GetProxy();
    SLE_ASSERT_AND_RETURN_LOG(proxy_, "get proxy error!");
}

SleHost::SleHostImpl::~SleHostImpl()
{
    SLE_LOGI("~SleHostImpl In");
    SLE_ASSERT_AND_RETURN_LOG(proxy_, "host proxy null error!");
    SLE_ASSERT_AND_RETURN_LOG(callbackImpl_, "callbackImpl null error!");
    SleErrorCode ret = proxy_->UnregisterHostCallback(callbackImpl_);
    SLE_ASSERT_AND_RETURN_LOG(ret == SLE_NO_ERROR, "unregister host callback failed(%{public}d)!", ret);
}

void SleHost::SleHostImpl::GetProxy()
{
    if (proxy_) {
        return;
    }
    sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    SLE_ASSERT_AND_RETURN_LOG(samgr, "no samgr error!");

    sptr<IRemoteObject> hostRemote = samgr->GetSystemAbility(KH_SLE_SERVICE_SYS_ABILITY_ID_);
    SLE_ASSERT_AND_RETURN_LOG(hostRemote, "no remote error!");

    SLE_ASSERT_AND_RETURN_LOG(deathRecipient_, "deathRecipient null error!");
    SLE_ASSERT_AND_RETURN_LOG(!((hostRemote->IsProxyObject()) && (!hostRemote->AddDeathRecipient(deathRecipient_))),
        "Failed to add death recipient");

    proxy_ = iface_cast<ISleHost>(hostRemote);
    SLE_ASSERT_AND_RETURN_LOG(proxy_, "no proxy error!");
    SleErrorCode ret = proxy_->RegisterHostCallback(callbackImpl_);
    SLE_ASSERT_AND_RETURN_LOG(ret == SLE_NO_ERROR, "register host callback failed(%{public}d)!", ret);
    SLE_LOGI("GetProxy success");
}

void SleHost::SleHostImpl::ResetService(const wptr<IRemoteObject> &remote)
{
    SLE_ASSERT_AND_RETURN_LOG(remote != nullptr, "remote null error!");
    std::lock_guard<std::mutex> lock(mutexProxy_);
    SLE_ASSERT_AND_RETURN_LOG(proxy_, "no proxy error!");
    sptr<IRemoteObject> serviceRemote = proxy_->AsObject();
    if ((serviceRemote != nullptr) && (serviceRemote == remote.promote())) {
        serviceRemote->RemoveDeathRecipient(deathRecipient_);
        proxy_ = nullptr;
        int tryTime = 5;
        while ((!proxy_) && tryTime > 0) {
            sleep(GET_PROXY_SLEEP_SEC);
            tryTime--;
            GetProxy();
        }
    }
}

SleErrorCode SleHost::SleHostImpl::CheckOperation()
{
    std::lock_guard<std::mutex> lock(mutexProxy_);
    GetProxy();
    SLE_ASSERT_AND_RETURN_RET_LOG(proxy_, SLE_ERR_INTERNAL_WRONG, "proxy null error!");
    return SLE_NO_ERROR;
}

SleHost::SleHost()
{
    SLE_LOGI("SleHost In");
    pimpl_ = std::make_unique<SleHostImpl>();
    SLE_ASSERT_AND_RETURN_LOG(pimpl_, "pimpl null error!");
}

SleHost::~SleHost()
{
    SLE_LOGI("~SleHost In");
}

SleHost &SleHost::GetDefaultHost()
{
    static SleHost hostInstance;
    return hostInstance;
}

SleErrorCode SleHost::EnableSle()
{
    SLE_LOGD("EnableSle In");
    if (!pimpl_) {
        SleHisysevent::GetInstance().ReportDfxEvent(ENABLE_SLE, SLE_ERR_INTERNAL_WRONG);
    }

    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SleHisysevent::GetInstance().ReportDfxEvent(ENABLE_SLE, ret);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->EnableSle();
    SleHisysevent::GetInstance().ReportDfxEvent(ENABLE_SLE, ret);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "enable sle failed(%{public}d)!", ret);

    std::unique_lock<std::mutex> lock(pimpl_->mtx_);
    auto status = pimpl_->conditionVariable_.wait_for(
        lock, std::chrono::milliseconds(3000), [this] { return pimpl_->ready_; });
    pimpl_->ready_ = false;
    if (!status) {
        SLE_LOGE("timeout, enable sle failed!");
        SleHisysevent::GetInstance().ReportDfxEvent(ENABLE_SLE, SLE_ERR_TIME_OUT);
        return SLE_ERR_TIME_OUT;
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_->state_ == SLE_HOST_STATE_ENABLED, SLE_ERR_FAILED, "enable sle failed(%s)!",
        SleDeviceStateStr(pimpl_->state_));
    return SLE_NO_ERROR;
}

SleErrorCode SleHost::DisableSle()
{
    SLE_LOGD("DisableSle In");

    if (!pimpl_) {
        SleHisysevent::GetInstance().ReportDfxEvent(DISABLE_SLE, SLE_ERR_INTERNAL_WRONG);
    }

    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SleHisysevent::GetInstance().ReportDfxEvent(DISABLE_SLE, ret);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->DisableSle();
    SleHisysevent::GetInstance().ReportDfxEvent(DISABLE_SLE, ret);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "disable sle failed(%{public}d)!", ret);

    std::unique_lock<std::mutex> lock(pimpl_->mtx_);
    auto status = pimpl_->conditionVariable_.wait_for(
        lock, std::chrono::milliseconds(3000), [this]{return pimpl_->ready_;});
    pimpl_->ready_ = false;
    if(!status){
        SLE_LOGE("timeout, disable sle failed!");
        SleHisysevent::GetInstance().ReportDfxEvent(DISABLE_SLE, SLE_ERR_TIME_OUT);
        return SLE_ERR_TIME_OUT;
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_->state_ == SLE_HOST_STATE_DISABLED, SLE_ERR_FAILED, "disable sle failed(%s)!",
        SleDeviceStateStr(pimpl_->state_));
    return SLE_NO_ERROR;
}

SleErrorCode SleHost::GetSleState(SleDeviceState &state)
{
    int32_t sleState;
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->GetSleState(sleState);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get sle state failed(%{public}d)!", ret);
    state = (SleDeviceState)sleState;
    return ret;
}

SleErrorCode SleHost::GetHostAddress(SleDeviceAddress &address)
{
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SleAddress addr;
    ret = pimpl_->proxy_->GetHostAddress(addr);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get host address failed(%{public}d)!", ret);
    address = addr.ConvertToSleDeviceAddress();
    return SLE_NO_ERROR;
}

SleErrorCode SleHost::SetHostName(const std::string name)
{
    SLE_ASSERT_AND_RETURN_RET_LOG(name.length() != 0, SLE_ERR_INVALID_PARAM, "param name null error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->SetHostName(name);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "set host name failed(%{public}d)!", ret);
    return SLE_NO_ERROR;
}

SleErrorCode SleHost::GetHostName(std::string &name)
{
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->GetHostName(name);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get host name failed(%{public}d)!", ret);
    return SLE_NO_ERROR;
}


SleErrorCode SleHost::GetHostDefaultConnectConfig(SleConnectConfig &hostConnParam)
{
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->GetHostDefaultConnectConfig(hostConnParam);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get host name failed(%{public}d)!", ret);
    return SLE_NO_ERROR;
}

SleErrorCode SleHost::StartFlowMonitor()
{
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->StartFlowMonitor();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "start flow monitor failed(%{public}d)!", ret);
    return SLE_NO_ERROR;
}

SleErrorCode SleHost::StopFlowMonitor()
{
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->StopFlowMonitor();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "stop flow monitor failed(%{public}d)!", ret);
    return SLE_NO_ERROR;
}

SleErrorCode SleHost::RegisterHostCallback(std::shared_ptr<SleHostCallback> callback)
{
    SLE_LOGD("RegisterHostCallback In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(callback != nullptr, SLE_ERR_INVALID_PARAM, "param callback nullptr error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_->callbacks_.Register(callback), SLE_ERR_FAILED,
        "register Host callback failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleHost::UnregisterHostCallback(std::shared_ptr<SleHostCallback> callback)
{
    SLE_LOGD("UnregisterHostCallback In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(callback != nullptr, SLE_ERR_INVALID_PARAM, "param callback nullptr error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(
        pimpl_->callbacks_.Unregister(callback), SLE_ERR_FAILED, "unregister Host callback failed!");
    return SLE_NO_ERROR;
}
} // SleStandard
} // NearLink
} // OHOS